﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vrpn;
using System.Windows.Forms;
using Ventuz.OSC;

namespace VRPN_OSC_Gateway
{
    public abstract class RemoteHandler
    {
        protected IVrpnObject sensorRemote;
        public bool ShowTrackingData { get; set; }
        public DataGridViewCell DataCell { get; set; }
        public string OscTarget { get; set; }
        public UdpWriter OscWriter { get; set; }
        internal void Update()
        {
            sensorRemote.Update();
        }
    }

    public class RemoteHandlerFactory
    {
        public static RemoteHandler Create(string type, string vrpnName, Connection connection)
        {
            if (type.Equals("Analog", StringComparison.InvariantCultureIgnoreCase))
            {
                return new AnalogRemoteHandler(vrpnName, connection);
            }
            if (type.Equals("Tracker", StringComparison.InvariantCultureIgnoreCase))
            {
                return new TrackerRemoteHandler(vrpnName, connection);
            }
            if (type.Equals("Button", StringComparison.InvariantCultureIgnoreCase))
            {
                return new ButtonRemoteHandler(vrpnName, connection);
            }
            if (type.Equals("VentuzLegacy", StringComparison.InvariantCultureIgnoreCase))
            {
                return new VentuzLegacyTrackerRemoteHandler(vrpnName, connection);
            }
            return null;
        }
    }

    public class AnalogRemoteHandler : RemoteHandler
    {
        public AnalogRemoteHandler(string vrpnName, Connection connection)
        {
            AnalogRemote analogRemote = new AnalogRemote(vrpnName, connection);
            analogRemote.AnalogChanged += new AnalogChangeEventHandler(analogRemote_AnalogChanged);
            this.sensorRemote = analogRemote;
        }

        private void analogRemote_AnalogChanged(object sender, AnalogChangeEventArgs e)
        {
            if (e.Channels.Length > 0)
            {
                if (ShowTrackingData)
                {
                    DataCell.Value = String.Join<double>(" ", e.Channels);
                }
                object[] args = new object[e.Channels.Length];
                for (int i = 0; i < e.Channels.Length; i++)
                {
                    args[i] = e.Channels[i];
                }
                OscBundle o = new OscBundle(e.Time, new OscElement(OscTarget, args));
                OscWriter.Send(o);
            }
        }
    }

    public class TrackerRemoteHandler : RemoteHandler
    {
        public TrackerRemoteHandler(string vrpnName, Connection connection)
        {
            TrackerRemote trackerRemote = new TrackerRemote(vrpnName, connection);
            trackerRemote.PositionChanged += new TrackerChangeEventHandler(trackerRemote_PositionChanged);
            this.sensorRemote = trackerRemote;
        }

        private void trackerRemote_PositionChanged(object sender, TrackerChangeEventArgs e)
        {
            if (ShowTrackingData)
            {
                DataCell.Value = "Sensor " + e.Sensor + " ist bei " + e.Position.ToString() + " / " + e.Orientation.ToString();
            }
            object[] p = new object[3];
            p[0] = e.Position.X;
            p[1] = e.Position.Y;
            p[2] = e.Position.Z;
            object[] q = new object[4];
            q[0] = e.Orientation.X;
            q[1] = e.Orientation.Y;
            q[2] = e.Orientation.Z;
            q[3] = e.Orientation.W;
            OscBundle o = new OscBundle(e.Time, new OscElement(OscTarget + "/Position" + e.Sensor, p), new OscElement(OscTarget + "/Quaternion" + e.Sensor, q));
            OscWriter.Send(o);
        }
    }

    public class ButtonRemoteHandler : RemoteHandler
    {
        public ButtonRemoteHandler(string vrpnName, Connection connection)
        {
            ButtonRemote buttonRemote = new ButtonRemote(vrpnName, connection);
            buttonRemote.ButtonChanged += new ButtonChangeEventHandler(buttonRemote_ButtonChanged);
            this.sensorRemote = buttonRemote;
        }

        private void buttonRemote_ButtonChanged(object sender, ButtonChangeEventArgs e)
        {
            if (ShowTrackingData)
            {
                DataCell.Value = e.Button.ToString() + (e.IsPressed ? " gedrückt" : " nicht gedrückt");
            }
            OscBundle o = new OscBundle(e.Time, new OscElement(OscTarget, e.Button, e.IsPressed));
            OscWriter.Send(o);
        }
    }

    public class VentuzLegacyTrackerRemoteHandler : RemoteHandler
    {
        public VentuzLegacyTrackerRemoteHandler(string vrpnName, Connection connection)
        {
            TrackerRemote trackerRemote = new TrackerRemote(vrpnName, connection);
            trackerRemote.PositionChanged += new TrackerChangeEventHandler(trackerRemote_PositionChanged);
            this.sensorRemote = trackerRemote;
        }

        private void trackerRemote_PositionChanged(object sender, TrackerChangeEventArgs e)
        {
            object[] p = new object[3];
            p[0] = e.Position.X * 100;
            p[1] = e.Position.Y * 100;
            p[2] = e.Position.Z * 100;
            Vector3 euler;
            e.Orientation.ToEuler(out euler);
            if (ShowTrackingData)
            {
                DataCell.Value = "Sensor " + e.Sensor + " ist bei " + e.Position.ToString() + " / " + euler.ToString();
            }
            OscBundle o = new OscBundle(e.Time, 
                new OscElement(OscTarget + "/POS" + e.Sensor, p), 
                new OscElement(OscTarget + "/WIN" + e.Sensor, euler.X, euler.Y, euler.Z));
            OscWriter.Send(o);
        }
    }
}
